package com.ffcs.crmd.tsp.server.core;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.sql.SQLException;
import java.util.Locale;
import java.util.Properties;

import org.apache.commons.lang3.StringUtils;
import org.quartz.SchedulerConfigException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.ffcs.crmd.tsp.common.constant.LoggerName;
import com.ffcs.crmd.tsp.common.exception.TspException;
import com.ffcs.crmd.tsp.server.api.ConnectionProvider;
import com.ffcs.crmd.tsp.server.api.Handler;
import com.ffcs.crmd.tsp.server.jdbc.DBConnectionManager;
import com.ffcs.crmd.tsp.server.jdbc.support.ServiceSupport;
import com.ffcs.crmd.tsp.server.jdbc.support.ServiceSupportTX;

/**
 * 
 * 功能说明:数据源处理中心
 *
 * @author ZHONGFUHUA
 * 
 * @since 2.0.0-SNAPSHOT
 *
 */
public class DataBaseHandler implements Handler {
    
    /**
     * 日志
     */
    private static final Logger    LOG           = LoggerFactory.getLogger(LoggerName.ServerLoggerName);
    
    /**
     * 
     */
    private final ServiceSupport   serviceSupport;
    
    /**
     * quartz处理中心
     */
    private QuartzSchedulerHandler quartzSchedulerHandler;
    
    private final static String    JDBC_PROVIDER = "providerClass";
    private final static String    JDBC_DRIVER   = "driver";
    private final static String    JDBC_URL      = "url";
    private final static String    JDBC_USERNAME = "username";
    private final static String    JDBC_PASSWORD = "password";
    
    public DataBaseHandler(Properties properties) throws TspException {
        try {
            String poolingProvider = properties.getProperty(JDBC_PROVIDER);
            String driver = properties.getProperty(JDBC_DRIVER);
            String url = properties.getProperty(JDBC_URL);
            String username = properties.getProperty(JDBC_USERNAME);
            String password = properties.getProperty(JDBC_PASSWORD);
            
            if (StringUtils.isBlank(driver)) {
                throw new TspException("Driver not specified for DataSource");
            }
            
            if (StringUtils.isBlank(url)) {
                throw new TspException("DB URL not specified for DataSource");
            }
            
            if (StringUtils.isBlank(username)) {
                throw new TspException("DB Username not specified for DataSource");
            }
            
            if (StringUtils.isBlank(password)) {
                throw new TspException("DB Password not specified for DataSource");
            }
            
            if (StringUtils.isBlank(poolingProvider)) {
                poolingProvider = "com.ffcs.crmd.tsp.server.jdbc.provider.JdbcConnectionProvider";
            }
            
            System.out.println("Using ConnectionProvider class '" + poolingProvider + "' for data source");
            ;
            
            try {
                ConnectionProvider connectionProvider = (ConnectionProvider) Class.forName(poolingProvider).newInstance();
                setBeanProps(connectionProvider, properties);
                DBConnectionManager.getInstance().setProvider(connectionProvider);
                serviceSupport = new ServiceSupportTX();
            } catch (Exception e) {
                throw new TspException("ConnectionProvider class '" + poolingProvider + "' could not be instantiated.", e);
            }
            
        } catch (Throwable sqle) {
            LOG.error("SERVER-DATABASE: Could not initialize DataSource:", sqle);
            throw new TspException("Could not initialize DataSource", sqle);
        }
    }
    
    /**
     * 
     * 功能说明:启动数据库连接
     * 
     * @throws TspException
     */
    @Override
    public void start() throws TspException {
        try {
            DBConnectionManager.getInstance().getProvider().initialize();
            serviceSupport.setQuartzSchedulerHandler(quartzSchedulerHandler);
        } catch (SQLException e) {
            new TspException(e);
        }
        LOG.info("SERVER-DATABASE: Data source handler start ok.");
        
    }
    
    /**
     * 
     * 功能说明:关闭数据库连接
     * 
     * @throws TspException
     */
    @Override
    public void shutdown() throws TspException {
        try {
            DBConnectionManager.getInstance().shutdown();
            LOG.info("SERVER-DATABASE: Data source handler shutdown ok.");
        } catch (SQLException e) {
            throw new TspException(e);
        }
    }
    
    /**
     * 功能说明: 获取serviceSupport
     *
     * @return serviceSupport serviceSupport
     */
    public ServiceSupport getServiceSupport() {
        return serviceSupport;
    }
    
    /**
     * 功能说明: 设置quartz处理中心
     *
     * @param quartzSchedulerHandler quartz处理中心 
     */
    public void setQuartzSchedulerHandler(QuartzSchedulerHandler quartzSchedulerHandler) {
        this.quartzSchedulerHandler = quartzSchedulerHandler;
    }
    
    private void setBeanProps(Object obj, Properties props)
        throws NoSuchMethodException, IllegalAccessException, java.lang.reflect.InvocationTargetException, IntrospectionException,
        SchedulerConfigException {
        props.remove("providerClass");
        
        BeanInfo bi = Introspector.getBeanInfo(obj.getClass());
        PropertyDescriptor[] propDescs = bi.getPropertyDescriptors();
        
        java.util.Enumeration<Object> keys = props.keys();
        while (keys.hasMoreElements()) {
            String name = (String) keys.nextElement();
            String c = name.substring(0, 1).toUpperCase(Locale.US);
            String methName = "set" + c + name.substring(1);
            
            java.lang.reflect.Method setMeth = getSetMethod(methName, propDescs);
            
            try {
                if (setMeth == null) {
                    continue;
                }
                
                Class<?>[] params = setMeth.getParameterTypes();
                if (params.length != 1) {
                    throw new NoSuchMethodException("No 1-argument setter for property '" + name + "'");
                }
                
                if (params[0].equals(int.class)) {
                    setMeth.invoke(obj, new Object[] {Integer.valueOf(props.getProperty(name)) });
                } else if (params[0].equals(long.class)) {
                    setMeth.invoke(obj, new Object[] {Long.valueOf(props.getProperty(name)) });
                } else if (params[0].equals(float.class)) {
                    setMeth.invoke(obj, new Object[] {Float.valueOf(props.getProperty(name)) });
                } else if (params[0].equals(double.class)) {
                    setMeth.invoke(obj, new Object[] {Double.valueOf(props.getProperty(name)) });
                } else if (params[0].equals(boolean.class)) {
                    setMeth.invoke(obj, new Object[] {Boolean.valueOf(props.getProperty(name)) });
                } else if (params[0].equals(String.class)) {
                    setMeth.invoke(obj, new Object[] {String.valueOf(props.getProperty(name)) });
                } else {
                    throw new NoSuchMethodException("No primitive-type setter for property '" + name + "'");
                }
            } catch (NumberFormatException nfe) {
                throw new SchedulerConfigException("Could not parse property '" + name + "' into correct data type: " + nfe.toString());
            }
        }
    }
    
    private java.lang.reflect.Method getSetMethod(String name, PropertyDescriptor[] props) {
        for (int i = 0; i < props.length; i++) {
            java.lang.reflect.Method wMeth = props[i].getWriteMethod();
            
            if (wMeth != null && wMeth.getName().equals(name)) {
                return wMeth;
            }
        }
        
        return null;
    }
    
}
